package com.walter.redis.tools;

import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Collections;
import java.util.Objects;

/**
 * 分布式锁服务
 * @author Walter
 * @date 2025/1/11 下午3:40
 **/
@Service
public class DistributedLockService {
    /**
     * LUA脚本，设置带有效期的分布式锁。
     */
    private final static String LUA_GET_LOCK_SCRIPT= "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then  return redis.call('expire',KEYS[1],ARGV[2])  else return 0 end";
    /**
     * LUA脚本，设置永久的分布式锁。
     */
    private final static String LUA_GET_NOT_EXPIRE_LOCK_SCRIPT= "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then return 1 else return 0 end";
    /**
     * LUA脚本，释放锁
     */
    private final static String LUA_RELEASE_LOCK_SCRIPT="if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
    /**
     * LUA脚本执行成功
     */
    private final static long EXEC_RESULT = 1L;
    /**
     *
     */
    private final static RedisSerializer argsSerializer = new StringRedisSerializer();
    /**
     *
     */
    private final static RedisSerializer resultSerializer = new StringRedisSerializer();
    /**
     *
     */
    @Resource
    RedisTemplate<String,Long> redisTemplate;
    /**
     * 尝试获取分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识，同时用于释放锁
     * @param expireTime 超期时间，单位：秒
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        Assert.state(expireTime>0,"锁过期时间必须大于0");
        DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(LUA_GET_LOCK_SCRIPT, Long.class);
        Long result = redisTemplate.execute(longDefaultRedisScript,argsSerializer,resultSerializer, Collections.singletonList(lockKey),requestId,String.valueOf(expireTime));
        return (Objects.nonNull(result) && result == EXEC_RESULT);

    }

    /**
     * 释放分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String requestId) {
        DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(LUA_RELEASE_LOCK_SCRIPT, Long.class);
        Long result = redisTemplate.execute(longDefaultRedisScript,argsSerializer,resultSerializer,Collections.singletonList(lockKey), requestId);
        return (Objects.nonNull(result) && result == EXEC_RESULT);
    }
}
